#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#include<fstream>
#include<ctime>
#include<set>
#include"Util.h"
#include"StringPool.h"
#include"DependencyEvaluator.h"
#include"DependencyParser.h"
#include"DependencyParser2.h"
using namespace std;

StringPool* pool = new StringPool(0);
set<string> punctSet = set<string>();
set<string> coordSet = set<string>();
set<string> commaSet = set<string>();

double Negative_Infinity = -1e100;
double Positive_Infinity = 1e100;

void Training_Online(ParserOptions *options){
	if(options->dev){
		DependencyPipe* pipe = NULL;
		if(options->order == 1){
			pipe = new DependencyPipe(options);
		}
		else if(options->order == 2){
			if(options->model == 1){
				pipe = new DependencyPipe2OSibling(options);
			}
			if(options->model == 2){
				pipe = new DependencyPipe2OGrandChildren(options);
			}
			if(options->model == 3){
				pipe = new DependencyPipe2O(options);
			}
		}
		else if(options->order == 3){
			if(options->model == 1){
				pipe = new DependencyPipe3OTSibling(options);
			}
			if(options->model == 2){
				pipe = new DependencyPipe3OGSibling(options);
			}
			if(options->model == 3){
				pipe = new DependencyPipe3O(options);
			}
		}
		else if(options->order == 4){
			if(options->model == 1){
				pipe = new DependencyPipe4OGTSibling(options);
			}
		}
		long start = clock() / CLOCKS_PER_SEC;
		vector<int>* instanceLengths = pipe->createInstances(options->trainfile, options->trainforest);
		long end = clock() / CLOCKS_PER_SEC;
		cout<<end - start<<endl;
		
		DependencyParser* dp = new DependencyParser(pipe, options);
		
		int numFeats = pipe->dataAlphabet.size();
		int numTypes = pipe->typeAlphabet.size();
		cout<<"Num Feats: "<<numFeats;
		cout<<".\tNum Edge Labels: "<<numTypes<<endl;

		dp->develop(instanceLengths, options->trainforest, options->devfile, options->logfile, options->goldfile);
		delete(instanceLengths);
		delete(dp);
	}
	else{
		if(options->train){

			DependencyPipe* pipe = NULL;
			if(options->order == 1){
				pipe = new DependencyPipe(options);
			}
			else if(options->order == 2){
				if(options->model == 1){
					pipe = new DependencyPipe2OSibling(options);
				}
				if(options->model == 2){
					pipe = new DependencyPipe2OGrandChildren(options);
				}
				if(options->model == 3){
					pipe = new DependencyPipe2O(options);
				}
			}
			else if(options->order == 3){
				if(options->model == 1){
					pipe = new DependencyPipe3OTSibling(options);
				}
				if(options->model == 2){
					pipe = new DependencyPipe3OGSibling(options);
				}
				if(options->model == 3){
					pipe = new DependencyPipe3O(options);
				}
			}
			else if(options->order == 4){
				if(options->model == 1){
					pipe = new DependencyPipe4OGTSibling(options);
				}
			}
			long start = clock();
			vector<int>* instanceLengths = pipe->createInstances(options->trainfile, options->trainforest);
			long end = clock();
			cout<<end - start<<endl;
		
			DependencyParser* dp = new DependencyParser(pipe, options);

			int numFeats = pipe->dataAlphabet.size();
			int numTypes = pipe->typeAlphabet.size();
			cout<<"Num Feats: "<<numFeats;
			cout<<".\tNum Edge Labels: "<<numTypes<<endl;

			dp->train(instanceLengths, options->trainforest);

			cout<<"Saving model...";
			dp->saveModel(options->modelName);
			cout<<"done.";
			delete(instanceLengths);
			delete(dp);
		}
	}
}

void Training_Offline(ParserOptions *options){
	int thread_num = options->thread_num;
	double C = options->C;
	double eta = options->eta;
	int maxiter = options->maxiter;
	int lm = options->lm;
	DependencyPipe* pipe = NULL;
	if(options->order == 1){
		pipe = new DependencyPipe(options);
	}
	else if(options->order == 2){
		if(options->model == 1){
			pipe = new DependencyPipe2OSibling(options);
		}
		if(options->model == 2){
			pipe = new DependencyPipe2OGrandChildren(options);
		}
		if(options->model == 3){
			pipe = new DependencyPipe2O(options);
		}
	}
	else if(options->order == 3){
		if(options->model == 1){
			pipe = new DependencyPipe3OTSibling(options);
		}
		if(options->model == 2){
			pipe = new DependencyPipe3OGSibling(options);
		}
		if(options->model == 3){
			pipe = new DependencyPipe3O(options);
		}
	}
	else if(options->order == 4){
		if(options->model == 1){
			pipe = new DependencyPipe4OGTSibling(options);
		}
	}

	DependencyParser2* dp = new DependencyParser2(pipe, options);

	if(!dp->learn(options->trainfile, options->modelName, C, eta, maxiter, thread_num, lm)){
		std::cerr << dp->what() << std::endl;
	}
	delete(dp);
}

int main(int argc, char* argv[]){
	//punctSet.insert("''");
	//punctSet.insert("``");
	//punctSet.insert(".");
	//punctSet.insert(":");
	//punctSet.insert(",");

	ifstream config_in(argv[1]);
	vector<string*> args;
	char line[100];
	while(config_in.getline(line, 100)){
		string arg = string(line);
		args.push_back(pool->get(arg));
	}
	ParserOptions* options = new ParserOptions(args);

	pool->addString(options->pool_size);
	int length = (int)(args.size());
	for(int i = 0; i < length; i++){
		pool->push_back(args[i]);
	}

	if(options->lm <= 1){
		Training_Online(options);
	}
	else{
		if(options->train){
			Training_Offline(options);
		}
	}

	if(options->test){
		DependencyPipe* pipe = NULL;
		if(options->order == 1){
			pipe = new DependencyPipe(options);
		}
		else if(options->order == 2){
			if(options->model == 1){
				pipe = new DependencyPipe2OSibling(options);
			}
			if(options->model == 2){
				pipe = new DependencyPipe2OGrandChildren(options);
			}
			if(options->model == 3){
				pipe = new DependencyPipe2O(options);
			}
		}
		else if(options->order == 3){
			if(options->model == 1){
				pipe = new DependencyPipe3OTSibling(options);
			}
			if(options->model == 2){
				pipe = new DependencyPipe3OGSibling(options);
			}
			if(options->model == 3){
				pipe = new DependencyPipe3O(options);
			}
		}
		else if(options->order == 4){
			if(options->model == 1){
				pipe = new DependencyPipe4OGTSibling(options);
			}
		}
		DependencyParser* dp = new DependencyParser(pipe, options);

		cout<<"\tLoading model...";
		dp->loadModel(options->modelName);
		cout<<"done."<<endl;
		
		pipe->closeAlphabets();

		dp->outputParses();
		
		delete(dp);
	}

	cout<<endl;

	if(options->eval){
		cout<<"\nEVALUATION PERFORMANCE... \n";
		DependencyEvaluator::evaluate(*(options->goldfile), *(options->outfile), *(options->format), options->labeled);
		printf("%s\n", "Done.");
	}
	delete(options);
	delete(pool);
	return 0;
}
